﻿module Parsing



open System
open System.IO
open Utility
open Sources
open Tokens
open Keywords
open Features
open Scenarios
open Steps
open ParsingEvents



let ParseStep(kind : StepKind, reader : SourceReader, context : ParsingContext) =
   let trigger = context :> IParsingTrigger      
   let location = reader.GetLocation()
   try      
      let step = ReadStep(reader, kind)
      let ended = reader.IsEnded()

      if (not ended && step.IsNone) then
         failwithf "Step '%A' not found" kind
      else
         let args = trigger.StepStarted(step.Value)

         if (args.Continue) then
            let args = trigger.StepEnded(step.Value)

            args.Continue
         else
            false
   with
      | _ as e -> trigger.ParsingError(e, location); false



// NEEDS MORE TESTS
let ParseScenario(reader : SourceReader, context : ParsingContext) =
   let trigger = context :> IParsingTrigger      
   let location = reader.GetLocation()
   try      
      let scenario = ReadScenarioHeader(reader)
      let ended = reader.IsEnded()

      if (not ended && scenario.IsNone) then
         failwith "Scenario not found"
      else         
         let args = trigger.ScenarioStarted(scenario.Value)

         if (args.Continue
            && ParseStep(StepKind.Given, reader, context) 
            && ParseStep(StepKind.When, reader, context)
            && ParseStep(StepKind.Then, reader, context)
            ) then
            let args = trigger.ScenarioEnded(scenario.Value)

            args.Continue                  
         else 
            false
   with
      | _ as e -> trigger.ParsingError(e, location); false



// NEEDS MORE TESTS
// TODO: test the seq aspect, reader loc should be on last failed execute.  all scenarios
//       should not be loaded then executed, but loaded as they are executed
let ParseFeature(reader : SourceReader, context : ParsingContext) =
   let trigger = context :> IParsingTrigger      
   let location = reader.GetLocation()
   try
      let feature = ReadFeatureHeader(reader)
      let ended = reader.IsEnded()

      if (not ended && feature.IsNone) then
         failwith "Feature not found"
      else
         let args = trigger.FeatureStarted(feature.Value)

         if (args.Continue) then            
            let scenarios = seq {                                             
               let another = true
               while (another) do                  
                  let another = ParseScenario(reader, context) && TryReadKeyword(reader, Keyword.Scenario)
                  yield another               
            }
         
            TilFalse(scenarios) |> ignore

         let args = trigger.FeatureEnded(feature.Value)            

         args.Continue
   with
      | _ as e -> trigger.ParsingError(e, location); false



// TODO: needs more tests
let ParseReader(reader : SourceReader, context : ParsingContext) =
   let trigger = context :> IParsingTrigger
   let location = reader.GetLocation()
   try   
      let features = seq {
         let another = true
         while (another) do
            let another = ParseFeature(reader, context) && TryReadKeyword(reader, Keyword.Feature)
            yield another
         }   

      TilFalse(features) |> ignore
   with
      | _ as e -> trigger.ParsingError(e, location); 



let ParseStream(stream : Stream, context : ParsingContext) =
   use reader = new SourceReader(stream)

   ParseReader(reader, context)



let ParseFile(file : FileInfo, context : ParsingContext) =
   use stream = file.OpenRead()

   ParseStream(stream, context)
   
